<?php
/**
 * @name SchemaManagerImpl
 * @author Dave
 * @link SchemaManager
 */

require_once('libraries/database/SchemaManager.php');
class SchemaManagerImpl implements SchemaManager {

	private $xml;
	private $data;
	private $tableName;
	private $tempSql;
	private $tableOptions;
	private $sql = array();
	private $alterSql = array();
	private $fields = array();
	private $sqlErrors = array();

	/**
	 * Constructor
	 */
	public function __construct() {
		$this->xml = xml_parser_create();
		xml_set_object($this->xml, &$this);
	}

	/**
	 * Get the SQL statement
	 */
	public function getSql() {
		return $this->sql;
	}

	/**
	 * Get the SQL errors
	 */
	public function getSqlErrors() {
		return $this->sqlErrors;
	}

	/**
	 * Get the data
	 */
	public function getData() {
		return $this->data;
	}

	/**
	 * Create an XML element
	 * @param resource $xml
	 * @param string $element
	 * @param array $attributes
	 */
	private function startElement($xml, $element, array $attributes) {
		if (!isset($attributes['DISABLED']) || $attributes['DISABLED'] == "no") {
			switch($element) {
				case 'DATABASE':
					if (isset($attributes['DROPIFEXISTS']) && $attributes['DROPIFEXISTS'] == "yes") {
						$this->sql[] = "DROP DATABASE IF EXISTS {$attributes['NAME']}";
					}
					if (isset($attributes['IFNOTEXISTS']) && $attributes['IFNOTEXISTS'] == "yes") {
						$ifNotExists = "IF NOT EXISTS ";
					} else {
						$ifNotExists = "";
					}
	                $this->sql[] = "CREATE DATABASE {$ifNotExists}{$attributes['NAME']}";
	                $this->sql[] = "USE {$attributes['NAME']}";
					break;
	
				case 'TABLE':
					if (isset($attributes['DROPIFEXISTS']) && $attributes['DROPIFEXISTS'] == "yes") {
						$this->sql[] = "DROP TABLE IF EXISTS {$attributes['NAME']}";
					}
					if (isset($attributes['TEMPORARY']) && $attributes['TEMPORARY'] == "yes") {
						$temporary = "TEMPORARY "; 
					} else {
						$temporary = "";
					}
					if (isset($attributes['IFNOTEXISTS']) && $attributes['IFNOTEXISTS'] == "yes") {
						$ifNotExists = "IF NOT EXISTS ";
					} else { 
						$ifNotExists = "";
					}
					if (isset($attributes['OPTIONS']) && $attributes['OPTIONS'] != "") {
						$this->table_options = " {$attributes['OPTIONS']}"; 
					} else {
						$this->table_options = "";
					}
					$this->tempSql = "CREATE {$temporary}TABLE {$ifNotExists}{$attributes['NAME']} (";
					$this->tableName = $attributes['NAME'];
					break;
	
				case 'FIELD':
					$tempVar = "{$attributes['NAME']} {$attributes['TYPE']}";
					if (isset($attributes['SIZE'])) {
						$tempVar .= "({$attributes['SIZE']})";
					}
					if (isset($attributes['NULL']) && $attributes['NULL'] == "no") {
						$tempVar.= " NOT NULL"; 
					} else {
						$tempVar .= " NULL";
					}
					if (isset($attributes['DEFAULT'])) {
						$tempVar .= " DEFAULT ";
						if ($attributes['DEFAULT'] == 'CURRENT_TIMESTAMP') {
							$tempVar .= $attributes['DEFAULT'];
						} else {
							$tempVar .= "'{$attributes['DEFAULT']}'";
						}
					}
					if (isset($attributes['EXTRA'])) {
						$tempVar .= " {$attributes['EXTRA']}";
					}
					$this->fields[] = $tempVar;
					break;
	
				case 'KEY':
					if (isset($attributes['TYPE']) && $attributes['TYPE'] == "primary") {
						$this->fields[] = "PRIMARY KEY ({$attributes['FIELD']})";
					}
					if (isset($attributes['TYPE']) && $attributes['TYPE'] == "unique") {
						$this->fields[] = "UNIQUE {$attributes['NAME']} ({$attributes['FIELD']})";
					}
					if (isset($attributes['TYPE']) && $attributes['TYPE'] == "index") {
						$this->fields[] = "INDEX {$attributes['NAME']} ({$attributes['FIELD']})";
					}
					break;
					
				case 'CONSTRAINT':
	                $tempVar = "ALTER TABLE {$attributes['ALTERTABLE']} ADD CONSTRAINT {$attributes['NAME']} FOREIGN KEY ({$attributes['FOREIGNKEY']}) REFERENCES {$attributes['REFTABLE']} ({$attributes['REFFIELD']})";
	                if (isset($attributes['DELETE']) && $attributes['DELETE'] == "yes") {
	                	$tempVar .= " ON DELETE CASCADE";
	                } else {
	                	$tempVar .= " ON DELETE NO ACTION";
	                }
	                if (isset($attributes['UPDATE']) && $attributes['UPDATE'] == "yes") {
	                	$tempVar .= " ON UPDATE CASCADE";
	                } else {
	                	$tempVar .= " ON UPDATE NO ACTION";
	                }
	
	                $this->alterSql[] = $tempVar;
					break;
	
				default:
					break;
			}
		}
	}

	/**
	 * Close the XML element
	 * @param resource $xml
	 * @param string $element
	 */
	private function endElement($xml, $element) {
		switch($element) {
			case 'TABLE':
				$this->sql[] = $this->tempSql.implode(', ', $this->fields) . ')' . $this->tableOptions;
				if (!empty($this->alterSql)) {
					$this->sql = array_merge((array) $this->sql, (array) $this->alterSql);
					$this->alterSql = array();
				}
				$this->tempSql = '';
				$this->fields = array();
				break;

			default:
				break;
		}
	}

	/**
	 * Retrieve the schema of the database
	 * @param string $data
	 * @param boolean $isFile
	 */
	public function setSchema($data, $isFile) {
		if ($isFile) {
			$fp = fopen($data, 'r');
			$this->data = fread($fp, filesize($data));
			fclose($fp);
		} else {
			$this->data = $data;
		}
	}

	/**
	 * Parse the xml schema
	 */
    public function parseSchema() {
		xml_parser_set_option($this->xml, XML_OPTION_CASE_FOLDING, TRUE);
		xml_set_element_handler($this->xml, 'startElement', 'endElement');
		xml_parse($this->xml, $this->data);
	}

    /**
     * Create MySQL database from the XML schema
     * @param string $host
     * @param string $user
     * @param string $pass
     * @param string $database
     */
	public function createDatabase(DBManager $dbManager = NULL, $host = NULL, $user = NULL, $pass = NULL, $database = NULL) {
		if (is_null($dbManager)) {
			$connection = mysql_connect($host, $user, $pass);
			if (!empty($database)) {
				mysql_select_db($database, $connection);
			}
			for ($i = 0; $i < count($this->sql); $i++) {
				mysql_query($this->sql[$i], $connection) or $this->sqlErrors[] = mysql_error($connection);
			}
			mysql_close($connection);
		} else {
			for ($i = 0; $i < count($this->sql); $i++) {
				$dbManager->Query($this->sql[$i]);
			}
		}
	}
}
?>